﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Translations.cs" company="ScRePt">
//   Copyright © ScRePt 2010 - 2012
// </copyright>
// <summary>
//   The translations.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace MP3D.Helper
{
    #region Using Directives

    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.IO;
    using System.Reflection;
    using System.Text.RegularExpressions;
    using System.Xml;

    using MediaPortal.GUI.Library;

    #endregion

    /// <summary>
    /// The translations.
    /// All the code is copy-paste from the excellent MyChitChat plugin
    /// Most of the plugin structure was also inspired by MyChitChat plugin
    /// </summary>
    public static class Translations
    {
        #region Constants and Fields

        /// <summary>
        /// The path.
        /// </summary>
        private static readonly string TranslationPath = string.Empty;

        /// <summary>
        /// The translate expr.
        /// </summary>
        private static readonly Regex TranslateExpr = new Regex(@"\$\{([^\}]+)\}");

        /// <summary>
        /// The localized strings.
        /// </summary>
        private static Dictionary<string, string> localizedStrings;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Initializes static members of the <see cref="Translations"/> class.
        /// </summary>
        static Translations()
        {
            string lang;

            try
            {
                lang = GUILocalizeStrings.GetCultureName(GUILocalizeStrings.CurrentLanguage());
            }
            catch (Exception)
            {
                lang = CultureInfo.CurrentUICulture.Name;
            }

            Log.Info("Using language " + lang);
            TranslationPath = GetPluginLanguagesPath();

            if (!System.IO.Directory.Exists(TranslationPath))
            {
                System.IO.Directory.CreateDirectory(TranslationPath);
            }

            if (!System.IO.File.Exists(Path.Combine(TranslationPath, "en.xml")))
            {
                Helper.SaveResourceToFilename(Path.Combine(TranslationPath, "en.xml"), "MP3D.Media.en.xml");
            }

            LoadTranslations(lang, out lang);
        }

        #endregion

        #region Properties

        /// <summary> 
        /// Gets the translated strings collection in the active language 
        /// </summary> 
        public static Dictionary<string, string> LocalizedStrings
        {
            get
            {
                if (localizedStrings == null)
                {
                    localizedStrings = new Dictionary<string, string>();
                    Type transType = typeof(Translations);
                    FieldInfo[] fields = transType.GetFields(BindingFlags.Public | BindingFlags.Static);
                    foreach (FieldInfo field in fields)
                    {
                        localizedStrings.Add(field.Name, !String.IsNullOrEmpty(field.GetValue(transType).ToString()) ? field.GetValue(transType).ToString() : field.Name);
                    }
                }

                return localizedStrings;
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// The create translation template.
        /// </summary>
        /// <param name="language">
        /// The language.
        /// </param>
        /// <returns>
        /// The create translation template.
        /// </returns>
        public static string CreateTranslationTemplate(string language)
        {
            string finalLang;
            LoadTranslations(language, out finalLang);
            string languageFilePath = Path.Combine(GetPluginLanguagesPath(), language + ".xml");
            using (XmlTextWriter writer = new XmlTextWriter(languageFilePath, System.Text.Encoding.UTF8))
            {
                writer.Formatting = Formatting.Indented;
                writer.WriteStartDocument();
                writer.WriteComment("MP3D translation file");
                writer.WriteComment("Language: " + language);
                writer.WriteComment("Note: English is the fallback for any strings not found in other languages");
                writer.WriteComment("Contributed by [Your Name]");
                writer.WriteComment("=========================================================================");
                writer.WriteStartElement("strings"); // <-- Important root element 
                foreach (KeyValuePair<string, string> currentField in LocalizedStrings)
                {
                    writer.WriteStartElement("string");
                    writer.WriteAttributeString("Field", currentField.Key);
                    writer.WriteValue(currentField.Value);
                    writer.WriteEndElement();
                }

                writer.WriteEndElement(); // <-- Closes it 
                writer.WriteEndDocument();
                writer.Flush();
                writer.Close();
            }

            return languageFilePath;
        }

        /// <summary>
        /// The enum to list.
        /// </summary>
        /// <typeparam name="T">
        /// </typeparam>
        /// <returns>
        /// </returns>
        /// <exception cref="ArgumentException">
        /// </exception>
        public static List<KeyValuePair<string, T>> EnumToList<T>()
        {
            Type enumType = typeof(T);

            // Can't use type constraints on value types, so have to do check like this 
            if (enumType.BaseType != typeof(Enum))
            {
                throw new ArgumentException("T must be of type System.Enum");
            }

            Array enumValArray = Enum.GetValues(enumType);

            List<KeyValuePair<string, T>> enumValList = new List<KeyValuePair<string, T>>(enumValArray.Length);

            foreach (int val in enumValArray)
            {
                T tmpType = (T)Enum.Parse(enumType, val.ToString());
                enumValList.Add(new KeyValuePair<string, T>(GetByName(tmpType.ToString()), tmpType));
            }

            return enumValList;
        }

        /// <summary>
        /// The get by name.
        /// </summary>
        /// <param name="name">
        /// The name.
        /// </param>
        /// <returns>
        /// The get by name.
        /// </returns>
        public static string GetByName(string name)
        {
            if (!LocalizedStrings.ContainsKey(name))
            {
                return name;
            }

            return LocalizedStrings[name];
        }

        /// <summary>
        /// The get by name.
        /// </summary>
        /// <param name="name">
        /// The name.
        /// </param>
        /// <param name="args">
        /// The args.
        /// </param>
        /// <returns>
        /// The get by name.
        /// </returns>
        public static string GetByName(string name, params object[] args)
        {
            return String.Format(GetByName(name), args);
        }

        /// <summary>
        /// The get culture languages.
        /// </summary>
        /// <returns>
        /// </returns>
        public static List<string> GetCultureLanguages()
        {
            CultureInfo[] cultureList = CultureInfo.GetCultures(CultureTypes.AllCultures);
            List<string> languages = new List<string>();

            foreach (CultureInfo current in cultureList)
            {
                languages.Add(current.Name);
            }

            return languages;
        }

        /// <summary>
        /// The get current culture language.
        /// </summary>
        /// <returns>
        /// The get current culture language.
        /// </returns>
        public static string GetCurrentCultureLanguage()
        {
            return GUILocalizeStrings.GetCultureName(GUILocalizeStrings.CurrentLanguage());
        }

        /// <summary>
        /// The load translations.
        /// </summary>
        /// <param name="lang">
        /// The lang.
        /// </param>
        /// <param name="finalLang">
        /// The final language.
        /// </param>
        /// <returns>
        /// The load translations.
        /// </returns>
        public static int LoadTranslations(string lang, out string finalLang)
        {
            localizedStrings = null;
            finalLang = lang;
            XmlDocument doc = new XmlDocument();
            Dictionary<string, string> translatedStrings = new Dictionary<string, string>();
            string langPath = string.Empty;
            try
            {
                langPath = Path.Combine(TranslationPath, lang + ".xml");
                doc.Load(langPath);

                if (doc.DocumentElement == null)
                {
                    throw new NullReferenceException();
                }
            }
            catch (Exception e)
            {
                if (lang == "en")
                {
                    return 0; // otherwise we are in an endless loop! 
                }

                if (e.GetType() == typeof(FileNotFoundException))
                {
                    Log.Warn("Cannot find translation file {0}.  Failing back to English", langPath);
                }
                else
                {
                    Log.Error(new Exception(String.Format("Error in translation xml file: {0}. Failing back to English", lang), e));
                }

                return LoadTranslations("en", out finalLang);
            }

            foreach (XmlNode stringEntry in doc.DocumentElement.ChildNodes)
            {
                if (stringEntry.NodeType == XmlNodeType.Element)
                {
                    if (stringEntry.Attributes == null)
                    {
                        Log.Error(new Exception("Error in Translation Engine"));
                    }
                    else
                    {
                        try
                        {
                            translatedStrings.Add(stringEntry.Attributes.GetNamedItem("Field").Value, stringEntry.InnerText);
                        }
                        catch (Exception ex)
                        {
                            Log.Error(new Exception("Error in Translation Engine", ex));
                        }
                    }
                }
            }

            Type transType = typeof(Translations);
            FieldInfo[] fieldInfos = transType.GetFields(BindingFlags.Public | BindingFlags.Static);

            foreach (FieldInfo fi in fieldInfos)
            {
                if (translatedStrings.ContainsKey(fi.Name))
                {
                    transType.InvokeMember(fi.Name, BindingFlags.SetField, null, transType, new object[] { translatedStrings[fi.Name] });
                }
                else
                {
                    Log.Info("Translation not found for field: {0}.  Using hard-coded English default.", fi.Name);
                }
            }

            return translatedStrings.Count;
        }

        /// <summary>
        /// 
        /// Takes an input string and replaces all ${named} variables with the proper translation if available 
        /// </summary>
        /// <param name="input">
        /// a string containing ${named} variables that represent the translation keys
        /// </param>
        /// <returns>
        /// translated input string
        /// </returns>
        public static string ParseString(string input)
        {
            MatchCollection matches = TranslateExpr.Matches(input);
            foreach (Match match in matches)
            {
                input = input.Replace(match.Value, GetByName(match.Groups[1].Value));
            }

            return input;
        }

        /// <summary>
        /// The translate enum to list.
        /// </summary>
        /// <typeparam name="T">
        /// </typeparam>
        /// <returns>
        /// </returns>
        /// <exception cref="ArgumentException">
        /// </exception>
        public static string[] TranslateEnumToList<T>()
        {
            Type enumType = typeof(T);

            // Can't use type constraints on value types, so have to do check like this 
            if (enumType.BaseType != typeof(Enum))
            {
                throw new ArgumentException("T must be of type System.Enum");
            }

            string[] enumValArray = Enum.GetNames(enumType);

            return enumValArray;
        }

        #endregion

        #region Methods

        /// <summary>
        /// The get plugin languages path.
        /// </summary>
        /// <returns>
        /// The get plugin languages path.
        /// </returns>
        public static string GetPluginLanguagesPath()
        {
            return MediaPortal.Configuration.Config.GetSubFolder(MediaPortal.Configuration.Config.Dir.Language, Helper.PluginName);
        }

        #endregion

        /* Plugin default strings - these are overridden by the user defined strings in the plugin config */

        public static string s2DDepth = "2D Depth";
        public static string AdjustParallax = "Adjust parallax";
        public static string Back = "Back";
        public static string BR = "Blue, red";
        public static string BY = "Blue, yellow";
        public static string CheckerBoardLF = "CheckerBoard, left first";
        public static string CheckerBoardRF = "CheckerBoard, right first";
        public static string ColAna = "Color anaglyph";
        public static string ColAnaBY = "Color anaglyph, blue / yellow";
        public static string ColAnaCR = "Color anaglyph, red / cyan";
        public static string ColAnaGP = "Color anaglyph, green / purple";
        public static string ColAnaPG = "Color anaglyph, purple / green";
        public static string ColAnaRC = "Color anaglyph, red / cyan";
        public static string ColAnaSourceFormat = "Color anaglyph source format";
        public static string ColAnaYB = "Color anaglyph, yellow / blue";
        public static string ColumnInterlaced = "Column interlaced";
        public static string ColumnInterlacedSourceFormat = "Column interlaced source format";
        public static string ContextMenu = "Context menu";
        public static string CR = "Cyan, red";
        public static string CurrentOutputFormatLabel = "Current output format is";
        public static string CurrentSourceFormatLabel = "Current source format is";
        public static string CycleOutputFormatsLabel = "Use Left / Right arrows to cycle between output formats";
        public static string CycleSourceFormatsLabel = "Use Left / Right arrows to cycle between source formats";
        public static string DecreaseHorizontal = "Decrease horizontal";
        public static string DecreaseHorizontalParallax = "Decrease horizontal parallax";
        public static string DecreaseOutputFormat = "Decrease output format";
        public static string DecreaseSourceFormat = "Decrease source format";
        public static string DecreaseVertical = "Decrease vertical";
        public static string DecreaseVerticalParallax = "Decrease vertical parallax";
        public static string Depth2D = "Depth 2D";
        public static string DetectLetterbox = "Detect letterbox";
        public static string DirectXDual = "DirectX Dual";
        public static string DlgDisable3DProcessingHeading = "Enabled 3D processing?";
        public static string DlgDisable3DProcessingLine1 = "Currently 3D processing is enabled.";
        public static string DlgDisable3DProcessingLine2 = "Do you want to disable it?";
        public static string DlgEnable3DProcessingHeading = "Disable 3D processing?";
        public static string DlgEnable3DProcessingLine1 = "Currently 3D processing is not enabled.";
        public static string DlgEnable3DProcessingLine2 = "Do you want to enable it?";
        public static string Dual = "Dual";
        public static string DualInput = "Dual stream";
        public static string DualStereoBright = "Dual, StereoBright";
        public static string FrameSequential = "Frame sequential";
        public static string FrameSequentialLF = "Frame sequential, left first";
        public static string FrameSequentialRF = "Frame sequential, right first";
        public static string FrameSequentialSourceFormat = "Frame sequential source format";
        public static string GP = "Green, purple";
        public static string GR = "Green, red";
        public static string GrayAna = "Gray anaglyph";
        public static string GrayAnaBY = "Gray anaglyph, blue / yellow";
        public static string GrayAnaCR = "Gray anaglyph, cyan / red";
        public static string GrayAnaGP = "Gray anaglyph, green / purple";
        public static string GrayAnaPG = "Gray anaglyph, purple / green";
        public static string GrayAnaRC = "Gray anaglyph, red / cyan";
        public static string GrayAnaSourceFormat = "Gray anaglyph source format";
        public static string GrayAnaYB = "Gray anaglyph, yellow / blue";
        public static string HColAna = "Half color anaglyph";
        public static string HColAnaBY = "Half color anaglyph, blue / yellow";
        public static string HColAnaCR = "Half color anaglyph, cyan / red";
        public static string HColAnaGP = "Half color anaglyph, green / purple";
        public static string HColAnaPG = "Half color anaglyph, purple / green";
        public static string HColAnaRC = "Half color anaglyph, red / cyan";
        public static string HColAnaSourceFormat = "Half color anaglyph source format";
        public static string HColAnaYB = "Half color anaglyph, yellow / blue";
        public static string HorizontalParallaxLabel = "Left/Right: Horizontal parallax";
        public static string IncreaseHorizontal = "Increase horizontal";
        public static string IncreaseHorizontalParallax = "Increase horizontal parallax";
        public static string IncreaseOutputFormat = "Increase output format";
        public static string IncreaseSourceFormat = "Increase source format";
        public static string IncreaseVertical = "Increase vertical";
        public static string IncreaseVerticalParallax = "Increase vertical parallax";
        public static string Interlaced = "Interlaced";
        public static string InterlacedLF = "Column interlaced, left first";
        public static string InterlacedLT = "Row interlaced, left top";
        public static string InterlacedRF = "Column interlaced, right first";
        public static string InterlacedRT = "Row interlaced, right top";
        public static string InterlacedSourceFormat = "Interlaced source format";
        public static string Iz3D2007 = "Iz3D2007";
        public static string Iz3D2009 = "Iz3D2009";
        public static string L = "Left";
        public static string LF = "Left first";
        public static string LT = "Left top";
        public static string Mode3D = "Mode 3D";
        public static string Mode3DContextMenu = "Mode 3D menu";
        public static string Mode3DTitleLabel = "Press F9 for menu, 0 for next mode or 3 for exit 3D mode";
        public static string MonoL = "Monoscopic, left first";
        public static string MonoR = "Monoscopic, right first";
        public static string Monoscopic = "Monoscopic";
        public static string MultiViewTiled = "Tiled (multiview)";
        public static string MultiViewTiledReversed = "Tiled (multiview), reversed";
        public static string MultiViewTiledSourceFormat = "Tiled (multiview) source format";
        public static string NextQuickMenu = "Next quick menu";
        public static string Normal = "Normal";
        public static string NVIDIA = "NVIDIA";
        public static string OPENGL = "OPENGL";
        public static string OptAna = "Optical anaglyph";
        public static string OptAnaBY = "Optical anaglyph, blue / yellow";
        public static string OptAnaCR = "Optical anaglyph, cyan / red";
        public static string OptAnaGP = "Optical anaglyph, green / purple";
        public static string OptAnaPG = "Optical anaglyph, purple / green";
        public static string OptAnaRC = "Optical anaglyph, red / cyan";
        public static string OptAnaSourceFormat = "Optical anaglyph source format";
        public static string OptAnaYB = "Optical anaglyph, yellow / blue";
        public static string Output2D = "Output 2D";
        public static string Output2DSourceFormat = "Output 2D source format";
        public static string OutputFormat = "Output format";
        public static string OverUnder = "Over / Under";
        public static string OverUnderLT = "Over / Under, left top";
        public static string OverUnderRT = "Over / Under, right top";
        public static string OverUnderSourceFormat = "Over / Under source format";
        public static string ParallaxHorizontically = "Horizontal parallax";
        public static string ParallaxVertically = "Vertical parallax";
        public static string PG = "Purple, green";
        public static string Profile = "Profile";
        public static string QuickMenuAdjustParallax = "Quick menu, adjust parallax";
        public static string QuickMenuOutputFormat = "Quick menu, output format";
        public static string QuickMenuSourceFormat = "Quick menu, source format";
        public static string R = "Right";
        public static string RB = "Red, blue";
        public static string RC = "Red, cyan";
        public static string RememberOutputFormat = "Remember output format for all sources";
        public static string RememberSourceFormat = "Remember source format for current source";
        public static string Reversed = "Reversed";
        public static string RF = "Right first";
        public static string RG = "Red, green";
        public static string RowInterlaced = "Row interlaced";
        public static string RowInterlacedSourceFormat = "Row interlaced source format";
        public static string RT = "Right top";
        public static string SelectOutputFormat = "Select output format";
        public static string SelectSourceFormat = "Select source format";
        public static string Sharp = "Sharp";
        public static string SideBySide = "Side by side";
        public static string SideBySideLF = "Side by side, left first";
        public static string SideBySideRF = "Side by side, right first";
        public static string SideBySideSourceFormat = "Side by side source format";
        public static string SIS = "SIS";
        public static string SoftPageFlip = "SoftPageFlip";
        public static string Source = "Source";
        public static string SourceFormat = "Source format";
        public static string StereoBright = "StereoBright";
        public static string StereoBrightL = "StereoBright, left first";
        public static string StereoBrightR = "StereoBright, right first";
        public static string StereoBrightSourceFormat = "StereoBright source format";
        public static string StereoscopicMenu = "Stereoscopic Menu";
        public static string TridelitySL = "TridelitySL";
        public static string TrueAna = "True anaglyph";
        public static string TrueAnaBR = "Pure anaglyph, blue / red";
        public static string TrueAnaGR = "Pure anaglyph, red / green";
        public static string TrueAnaRB = "Pure anaglyph, red / blue";
        public static string TrueAnaRG = "Pure anaglyph, red / green";
        public static string TrueAnaSourceFormat = "True anaglyph source format";
        public static string UpdateProfile = "Update profile";
        public static string UpdateSubtitles = "Update subtitles";
        public static string VerticalParallaxLabel = "Up / Down : Vertical parallax";
        public static string YB = "Yellow, blue";        
    }
}